#pragma once

#include <unordered_map>
#include <pthread.h>
#include "util.hpp"
#include "config.hpp"

// 服务端数据管理模块实现
// 管理服务器上所有备份的文件的相关信息，以便于随时获取
namespace cloud
{
    // 将所有需要的数据信息封装成一个结构体
    struct BackupInfo
    {
        bool _pack_flag;        // 文件是否压缩的标志位
        size_t _file_size;      // 文件大小
        time_t _modify_time;    // 最后一次修改时间
        time_t _access_time;    // 最后一次访问时间
        std::string _real_path; // 备份文件的存储路径
        std::string _pack_path; // 压缩包存储路径
        std::string _url_path;  // url中请求资源的路径

        // 传入文件路径，初始化该文件的所有信息
        bool InitBackupInfo(const std::string &real_path)
        {
            FileUtil fu(real_path);
            if (fu.Exists() == false)
            {
                std::cout << "new backupinfo: file not exists!\n";
                return false;
            }

            _pack_flag = false;
            _file_size = fu.FileSize();
            _modify_time = fu.LastMTime();
            _access_time = fu.LastATime();
            _real_path = real_path;

            // 压缩包存储目录，文件下载请求的url前缀需要从配置文件获取
            Config *config = Config::GetInstance();                    // 获取一个配置文件加载的单例对象
            std::string packdir = config->GetPackDir();                // 获取压缩包存储目录
            std::string packsuffix = config->GetPackFileSuffix();      // 获取压缩包后缀
            std::string download_prefix = config->GetDownloadPrefix(); // 获取文件下载请求的url前缀

            // 例： ./backdir/a.txt   ->   ./packdir/a.txt.lz
            _pack_path = packdir + fu.FileName() + packsuffix;

            // ./backdir/a.txt   ->	  /download/a.txt
            _url_path = download_prefix + fu.FileName();
            return true;
        }
    };

    // 数据信息管理类：管理所有文件的信息
    class DataManager
    {
    public:
        // 构造
        DataManager()
        {
            // 存储备份文件信息的文件在配置文件中指定，所以这里获取即可
            _backup_file = Config::GetInstance()->GetBackupFile();
            // 初始化读写锁
            // int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
            // 第一个参数传读写锁的地址，第二个参数指定读写锁的属性，通常传NULL，使用默认属性即可
            pthread_rwlock_init(&_rwlock, NULL);
            InitLoad(); // 将数据加载到内存
        }

        // 析构
        ~DataManager()
        {
            // 销毁读写锁：int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
            pthread_rwlock_destroy(&_rwlock);
        }

        // 插入
        bool Insert(const BackupInfo &info)
        {
            // 插入要修改临界资源，是“写”操作，所以这里要加锁保证线程安全
            pthread_rwlock_wrlock(&_rwlock); // 写锁加锁
            // STL不是线程安全的（STL的设计初衷是将性能挖掘到极致,一旦涉及到加锁,将会对性能造成巨大的影响）
            _table[info._url_path] = info;   // 插入到哈希表中
            pthread_rwlock_unlock(&_rwlock); // 解锁
            Storage();                       // 有数据新增，进行一下持久化存储
            return true;
        }

        // 更新/修改
        bool Update(const BackupInfo &info)
        {
            // 更新也是“写”操作
            pthread_rwlock_wrlock(&_rwlock); // 写锁加锁
            // 更新（unordered_map不允许键值冗余，之间插入就会覆盖之前的数据）
            _table[info._url_path] = info;
            pthread_rwlock_unlock(&_rwlock); // 解锁
            Storage();                       // 有数据更新，进行一下持久化存储
            return true;
        }

        // 通过url获取对应文件的信息
        bool GetOneByURL(const std::string &url, BackupInfo &info)
        {
            // 这里只是访问共享资源（“读”操作），所以加读锁
            pthread_rwlock_rdlock(&_rwlock); // 读锁加锁
            // std::unordered_map<std::string, BackupInfo>::iterator  it=_table.find(url);
            auto it = _table.find(url);
            // url是key值，可以直接find
            if (it == _table.end()) // 没有找到

            {
                pthread_rwlock_unlock(&_rwlock);
                return false;
            }
            info = it->second;
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        // 通过文件的实际存储路径获取对应文件的信息
        bool GetOneByRealPath(const std::string &real_path, BackupInfo &info)
        {
            pthread_rwlock_rdlock(&_rwlock); // 读锁加锁
            // real_path不是key值，所以这里得遍历查找
            for (auto it : _table)
            {
                if (it.second._real_path == real_path)
                {
                    info = it.second;
                    pthread_rwlock_unlock(&_rwlock);
                    return true;
                }
            }
            pthread_rwlock_unlock(&_rwlock);
            return false;
        }

        // 获取所有文件信息存入一个vector中
        bool GetAll(std::vector<BackupInfo> &v)
        {
            pthread_rwlock_rdlock(&_rwlock); // 读锁加锁
            for (auto it : _table)
            {
                v.push_back(it.second);
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        // 将现有的文件信息数据进行持久化存储到文件中
        bool Storage()
        {
            // 1.获取所有数据
            std::vector<BackupInfo> v;
            GetAll(v);

            // 2.将所有数据放入一个json::value数组中
            Json::Value root;
            for (auto e : v)
            {
                Json::Value tmp;
                tmp["pack_flag"] = e._pack_flag;
                //_file_size的类型是size_t，jsoncpp不支持这种类型，强转一下
                tmp["file_size"] = (Json::Int64)e._file_size;
                tmp["access_time"] = (Json::Int64)e._access_time;
                tmp["modify_time"] = (Json::Int64)e._modify_time;
                tmp["real_path"] = e._real_path;
                tmp["pack_path"] = e._pack_path;
                tmp["url_path"] = e._url_path;
                // Json::Value 是一个非常灵活的数据类型，它可以表示 JSON 中的各种值
                // 包括对象（键值对集合）、数组、字符串、数字、布尔值和空值
                // 调用 append 方法时，它将自动转换为一个数组
                root.append(tmp);
            }

            // 3.将json格式的数据对象序列化成字符串
            std::string body;
            JsonUtil::Serialize(root, body);

            // 4.将序列化后的json格式字符串写入文件
            FileUtil fu(_backup_file);
            fu.SetContent(body);

            return true;
        }

        // 初始化加载：将持久化存储的文件信息数据加载到内存中，便于我们获取
        // 获取的时候要使用数据管理类的接口，所以该函数在构造中调用即可，构造时直接加载数据
        bool InitLoad()
        {
            // 1.将文件中的数据读取到字符串中
            FileUtil fu(_backup_file);
            if (fu.Exists() == false)
                return true;
            std::string body;
            fu.GetContent(body);

            // 2.将字符串反序列化成Json::value
            Json::Value root;
            JsonUtil::UnSerialize(body, root);

            // 3.将Json::value中的数据添加到哈希表中（加载到内存）
            // 持久化是存入的json类型是数组
            for (auto e : root)
            {
                BackupInfo info;
                info._pack_flag = e["pack_flag"].asBool();
                info._file_size = e["file_size"].asInt64();
                info._access_time = e["access_time"].asInt64();
                info._modify_time = e["modify_time"].asInt64();
                info._real_path = e["real_path"].asString();
                info._pack_path = e["pack_path"].asString();
                info._url_path = e["url_path"].asString();
                // 插入哈希表
                Insert(info);
            }
            return true;
        }

    private:
        std::string _backup_file; // 存储备份文件信息的文件
        // 内存中以哈希表存储，查询速度快(以文件访问URL为key，数据信息结构为val)
        std::unordered_map<std::string, BackupInfo> _table;
        // 这些数据可能被不同模块多线程访问，但通常读多写少，如果使用互斥锁会导致效率大打折扣
        // 所以这里使用读写锁来保证线程安全
        // C++标准库没有支持读写锁，这里我们使用原生线程库中的读写锁
        pthread_rwlock_t _rwlock; // 读写锁（读共享，写互斥，默认读者优先）
    };
}